/*
 * Copyright (c) 2009 Frederic Daoud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.stripesbook.rayures.view;

import java.util.ArrayList;
import java.util.List;
import net.sourceforge.stripes.controller.StripesFilter;
import org.stripesbook.rayures.util.MapBundle;

/**
 * Base class for creating view objects. A view object can be used as a container, because it has
 * children and the {@link #toString()} method has a default behavior of rendering the children.
 *
 * <p>
 * This class also holds a reference to the {@link ViewContext} object and the aggregation of
 * resource bundles, under the {@code protected} variables {@code viewContext} and {@code bundle},
 * respectively.
 * </p>
 *
 * @author Frederic Daoud
 */
public class ViewObject<V extends ViewObject<?>> {

  private List<Object> children = new ArrayList<Object>();
  private ViewObject<?> parent;
  protected ViewContext viewContext;
  protected MapBundle bundle;

  public void setViewContext(ViewContext viewContext) {
    this.viewContext = viewContext;
    this.bundle = viewContext.getBundle();
  }

  public ViewContext getViewContext() {
    return viewContext;
  }

  /**
   * Method called after object construction, to be overridden by subclasses. No need to call
   * {@code super.onInit()} because this implementation is empty.
   */
  public void onInit() {
  }

  public V add(Object... childObjects) {
    V result = null;
    for (Object child : childObjects) {
      result = addAt(children.size(), child);
    }
    return result;
  }

  public V insert(Object child) {
    return addAt(0, child);
  }

  private V addAt(int index, Object child) {
    children.add(index, child);
    if (child instanceof ViewObject<?>) {
      ((ViewObject<?>) child).setParent(this);
    }
    return getThis();
  }

  public List<Object> getChildren() {
    return children;
  }

  @SuppressWarnings("unchecked")
  protected V getThis() {
    return (V) this;
  }

  public ViewObject<?> getParent() {
    return parent;
  }
  public void setParent(ViewObject<?> parent) {
    this.parent = parent;
  }

  /**
   * Renders the children of this view object.
   */
  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    for (Object child : children) {
      builder.append(child.toString());
    }
    return builder.toString();
  }

  /**
   * Convenience method.
   */
  @SuppressWarnings("unchecked")
  public <T extends ViewObject<?>> T tag(String type) {
    try {
      return tag((Class<T>) Class.forName(type), viewContext);
    }
    catch (Exception exc) {
      throw new RuntimeException("Could not create tag of type: " + type, exc);
    }
  }

  /**
   * Convenience method.
   */
  public <T extends ViewObject<?>> T tag(Class<T> type) {
    return tag(type, viewContext);
  }

  public static <T extends ViewObject<?>> T tag(Class<T> type, ViewContext viewContext) {
    T viewObject = StripesFilter.getConfiguration().getObjectFactory().newInstance(type);

    viewObject.setViewContext(viewContext);
    viewObject.onInit();

    return viewObject;
  }
}
